#region MIT Licence
/*
 * Copyright (C) 2014 PdfCreative
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software 
 * and associated documentation files (the "Software"), to deal in the Software without restriction, 
 * including without limitation the rights to use, copy, modify, merge, publish, distribute, 
 * sublicense, and/or sell copies of the Software, and to permit persons to whom 
 * the Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included 
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE 
 * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
#endregion

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;

namespace PdfCreative.Pdf.Writer.Embedding
{
    /// <summary>
    /// The PdfLexer class reads tokens off a PDF document stream. It used by the PdfParser to embed a PDF document into the Writer's
    /// main PDF stream. The PdfLexer operates in two possible modes - in the PDF mode it reads text tokens off the stream and interprets these
    /// so it can provide clients with accurate information on the currently read token at each step.
    /// In the Binary mode, the PdfLexer reads binary tokens off the stream and passes them on without attempting to parse them.
    /// </summary>
    internal class PdfLexer
    {
        // Private constants
        // =================
        #region Private constants
        private const int BUFFER_SIZE =                 0x20000;
        private const byte WHITESPACE_ZERO =            (byte)'\0';
        private const byte WHITESPACE_TAB =             (byte)'\t';
        private const byte WHITESPACE_10 =              (byte)'\xa';
        private const byte WHITESPACE_12 =              (byte)'\xc';
        private const byte WHITESPACE_13 =              (byte)'\xd';
        private const byte WHITESPACE_CR =              (byte)'\r';
        private const byte WHITESPACE_LF =              (byte)'\n';
        private const byte WHITESPACE_SPACE =           (byte)' ';
        private const byte COMMENT_START =              (byte)'%';
        private const byte ARRAY_OPEN =                 (byte)'[';
        private const byte ANGLE_OPEN =                 (byte)'<';
        private const byte ARRAY_CLOSE =                (byte)']';
        private const byte ANGLE_CLOSE =                (byte)'>';
        private const byte NAME_START =                 (byte)'/';
        private const byte STRING_OPEN =                (byte)'(';
        private const byte STRING_CLOSE =               (byte)')';
        private const byte STRING_ESCAPE =              (byte)'\\';
        private const byte OBJECT_REFERENCE =           (byte)'R';
        #endregion



        // Private fields
        // ==============
        #region Private fields
        private Stream _stream;                         // The source stream
        private PdfTokenType _tokenType = PdfTokenType.Null;
                                                        // The current token type
        private PdfLexerMode _mode = PdfLexerMode.Pdf;
                                                        // The mode of the lexer
        private Byte[] _buffer;                         // Read buffer
        private int _startPosition;                     // The start position of the current token
        private int _currentPosition;                   // The current position within the stream
        private int _endOfStream;                       // The end of the stream
        private Stack<long> _stateStack;                 // The preserved states.
        #endregion



        // Public properties
        // =================
        #region Public properties
        /// <summary>
        /// Retrieves the current buffer.
        /// </summary>
        public byte[] Buffer
        {
            get { return _buffer; }
        }

        /// <summary>
        /// Sets or retrieves the current mode that the lexer is in.
        /// </summary>
        public PdfLexerMode Mode
        {
            get { return _mode; }
            set { _mode = value; }
        }

        /// <summary>
        /// Retrieves the length of the current token
        /// </summary>
        public int TokenLength
        {
            get { return _currentPosition - _startPosition; }
        }

        /// <summary>
        /// Retrieves the start position of the current token
        /// </summary>
        public int TokenStart
        {
            get { return _startPosition; }
        }

        /// <summary>
        /// Retrieves the type of the token that was just read.
        /// </summary>
        public PdfTokenType TokenType
        {
            get { return _tokenType; }
        }

        /// <summary>
        /// Retrieves the token's string value.
        /// This converts the token's bytes to a string instance.
        /// </summary>
        public string Value
        {
            get { return System.Text.ASCIIEncoding.ASCII.GetString(_buffer, _startPosition, _currentPosition - _startPosition); }
        }
        #endregion



        // Public methods
        // ==============
        #region Public methods
        /// <summary>
        /// fetches a block of given length.
        /// </summary>
        /// <param name="length">The length</param>
        /// <returns>The length, clipped to the end of the stream if necessary.</returns>
        public int FetchBlock(int length)
        {
            if (_currentPosition >= BUFFER_SIZE)
            {
                _currentPosition = 0;
                int bytesRead = _stream.Read(_buffer, 0, BUFFER_SIZE);
                _endOfStream = _currentPosition + bytesRead;
            }

            int jumpLength = Math.Min(length, _endOfStream - _currentPosition);
            _startPosition = _currentPosition;
            _currentPosition += jumpLength;

            return jumpLength;
        }

        /// <summary>
        /// Opens a stream for a set number of bytes. This method creates a new PdfLexerStream instance that
        /// wraps the lexer's underlying stream, and returns it. The stream only operates over the specified
        /// number of bytes.
        /// Opening a stream invalidates the state of the lexer. It is essential that the next operation on the
        /// lexer is Seek(). Using the Lexer at the same time as an opened stream is not supported.
        /// </summary>
        /// <returns>The stream.</returns>
        public PdfLexerStream OpenStream(int length)
        {
            return new PdfLexerStream(this, length);
        }

        /// <summary>
        /// Preserves the lexer's current state. To restore this state, use RestoreState().
        /// </summary>
        public void PreserveState()
        {
            // We determine the actual position of the lexer in the source stream.
            // This is the stream's current position, minus the number of bytes in the buffer,
            // plus the current offset in the buffer.
            long realPosition = _stream.Position - _endOfStream + _currentPosition;

            // Preserve this position.
            _stateStack.Push(realPosition);
        }

        /// <summary>
        /// Reads the next token off the stream, and indicates if there are more tokens left in the stream.
        /// </summary>
        /// <returns>A value that indicates if there are more tokens to be read.</returns>
        public bool Read()
        {
                return ReadFromStream();
        } // Read()

        /// <summary>
        /// Moves the lexer forward to the specified location. This method returns a value that indicates
        /// success or failure. If the lexer's current position is beyond the desired position, the method
        /// returns False. If the desired position is the same as the lexer's current position, the result
        /// is True. If not enough bytes are available, the method returns False.
        /// </summary>
        /// <param name="position">The position to move forward to.</param>
        /// <returns>A boolean value indicating that the bytes could be read (True)
        /// or not.</returns>
        public bool ReadAhead(long position)
        {
            bool result = false;

            long positionInStream = _stream.Position - _endOfStream + _currentPosition;
            int bytesToRead = (int)( position - positionInStream);

            if (bytesToRead >= 0)
            {
                while (bytesToRead > 0)
                {
                    // If we've fallen off the buffer we need to shuffle the current token to its beginning
                    // and fetch more data.
                    if (_currentPosition >= BUFFER_SIZE)
                    {
                        Array.Copy(_buffer, _startPosition, _buffer, 0, _currentPosition - _startPosition);
                        _currentPosition -= _startPosition;
                        _startPosition = 0;
                        int bytesRead = _stream.Read(_buffer, _currentPosition, BUFFER_SIZE - _currentPosition);
                        _endOfStream = _currentPosition + bytesRead;
                    }

                    // We stop if can go no further.
                    if (_currentPosition >= _endOfStream) break;

                    // .. and the next byte
                    _currentPosition++;
                    --bytesToRead;
                }

                result = 0 == bytesToRead;
            }

            return result;

        } // ReadAhead()

        /// <summary>
        /// Reads the next token from the stream.
        /// </summary>
        /// <returns>The value of the next token, or null if there are no more tokens to be read.</returns>
        public string ReadNextToken()
        {
            string token = null;

            while (null==token && this.Read())
            {
                if (PdfTokenType.Token == this.TokenType || PdfTokenType.Name == this.TokenType || PdfTokenType.Number == this.TokenType)
                    token = this.Value;
            }
            return token;
        }

        /// <summary>
        /// Reads past any whitespace token to the next non-whitespace token.
        /// </summary>
        public void ReadPastWhitespace()
        {
            while (this.Read() && PdfTokenType.Whitespace == _tokenType) ;
        }

        /// <summary>
        /// Restores a previously preserved state. This resets the lexer's buffer and points the stream at the
        /// preserved location.
        /// </summary>
        public void RestoreState()
        {
            if (_stateStack.Count > 0)
            {
                _stream.Seek(_stateStack.Pop(), SeekOrigin.Begin);
                _startPosition = BUFFER_SIZE;
                _currentPosition = BUFFER_SIZE;
            }
        }

        /// <summary>
        /// Performs a seek on the stream.
        /// </summary>
        /// <param name="offset">The offset to seek to.</param>
        /// <param name="origin">The location to seek from.</param>
        public void Seek(long offset, SeekOrigin origin)
        {
            System.Diagnostics.Debug.Assert(SeekOrigin.Current != origin);

            if (SeekOrigin.End == origin || offset >= _stream.Position || offset < (_stream.Position - _endOfStream))
            {
                _stream.Seek(offset, origin);
                _startPosition = BUFFER_SIZE;
                _currentPosition = BUFFER_SIZE;
            }
            else
            {
                _currentPosition = _endOfStream - (int)(_stream.Position - offset);
            }
        }
        #endregion



        // Private implementation
        // ======================
        #region Private implementation
        /// <summary>
        /// Reads the next token off the source stream and returns a value indicating if the read was successful.
        /// </summary>
        /// <returns>A value indicating if the read was successful (true) or not</returns>
        private bool ReadFromStream()
        {
            bool finished = false;
            LexerReadState readState = LexerReadState.Free;
            byte current = 0;
            byte previous = 0;
            bool isNumber = false;
            int nestedParentheses = 0;
            _startPosition = _currentPosition;
            switch (_mode)
            {
                case PdfLexerMode.Pdf:
                    // In the PDF mode we parse PDF tokens.
                    while (false == finished)
                    {
                        // If we've fallen off the buffer we need to shuffle the current token to its beginning
                        // and fetch more data.
                        if (_currentPosition >= BUFFER_SIZE)
                        {
                            Array.Copy(_buffer, _startPosition, _buffer, 0, _currentPosition - _startPosition);
                            _currentPosition -= _startPosition;
                            _startPosition = 0;
                            int bytesRead = _stream.Read(_buffer, _currentPosition, BUFFER_SIZE - _currentPosition);
                            _endOfStream = _currentPosition + bytesRead;
                        }

                        if (_currentPosition >= _endOfStream) break;
                        previous = current;
                        current = _buffer[_currentPosition];

                        switch (readState)
                        {
                            case LexerReadState.Free:
                                // In the free state, we handle the following characters:
                                // whitespace - we enter the whitespace state
                                // / - we enter the token state
                                // % - we enter the comment state
                                // < - we enter the AfterAngleOpen state
                                // > - we enter the AfterAngleClose state
                                // ( - we enter the string state
                                // [ - we have found an ArrayStart token
                                // ] - we have found an ArrayEnd token
                                // any other - we enter the token state.
                                _currentPosition++;
                                switch (current)
                                {
                                    case WHITESPACE_ZERO:
                                    case WHITESPACE_TAB:
                                    case WHITESPACE_10:
                                    case WHITESPACE_12:
                                    case WHITESPACE_13:
                                    case WHITESPACE_SPACE:
                                        readState = LexerReadState.WhiteSpace;
                                        break;

                                    case NAME_START:
                                        readState = LexerReadState.Token;
                                        break;

                                    case COMMENT_START:
                                        readState = LexerReadState.Comment;
                                        break;

                                    case ANGLE_OPEN:
                                        readState = LexerReadState.AfterAngleOpen;
                                        break;

                                    case ANGLE_CLOSE:
                                        readState = LexerReadState.AfterAngleClose;
                                        break;

                                    case STRING_OPEN:
                                        readState = LexerReadState.String;
                                        nestedParentheses = 0;
                                        break;

                                    case ARRAY_OPEN:
                                        _tokenType = PdfTokenType.ArrayStart;
                                        finished = true;
                                        break;

                                    case ARRAY_CLOSE:
                                        _tokenType = PdfTokenType.ArrayEnd;
                                        finished = true;
                                        break;

                                    default:
                                        readState = LexerReadState.Token;
                                        isNumber = current >= 48 && current <= 57;
                                        break;
                                }
                                break;

                            case LexerReadState.String:
                                // In the string state we read until we find a ) token. The ) token
                                // indicates the end of the string but only if it is not preceded by a '\'
                                // token or matched by a previous ( token.
                                _currentPosition++;
                                switch (current)
                                {
                                    case STRING_OPEN:
                                        if (STRING_ESCAPE != previous)
                                            nestedParentheses++;
                                        break;

                                    case STRING_CLOSE:
                                        if (STRING_ESCAPE != previous && --nestedParentheses < 0)
                                        {
                                            _tokenType = PdfTokenType.String;
                                            finished = true;
                                        }
                                        break;

                                    default:
                                        break;
                                }
                                break;

                            case LexerReadState.Comment:
                                // When we are in the comment state we read until we have read a \r\n sequence.
                                _currentPosition++;
                                if (WHITESPACE_CR == (char)current && WHITESPACE_LF == previous)
                                {
                                    _tokenType = PdfTokenType.Comment;
                                    finished = true;
                                }
                                break;

                            case LexerReadState.Token:
                                // In the token state, we continue to read data until we find:
                                // - whitespace: we end the current token
                                // - %: we end the current token
                                // - [: we end the current token
                                // - <: we end the current token
                                // - >: we end the current token
                                // - ]: we end the current token
                                // - /: we end the current token
                                // - (: we end the current token
                                switch (current)
                                {
                                    case WHITESPACE_ZERO:
                                    case WHITESPACE_TAB:
                                    case WHITESPACE_10:
                                    case WHITESPACE_12:
                                    case WHITESPACE_13:
                                    case WHITESPACE_SPACE:
                                    case COMMENT_START:
                                    case ARRAY_OPEN:
                                    case ANGLE_OPEN:
                                    case ARRAY_CLOSE:
                                    case ANGLE_CLOSE:
                                    case NAME_START:
                                    case STRING_OPEN:
                                        // If this token is a number, we push it onto the queue.
                                        // We are not finished until we find a non-numeric token.
                                        if (isNumber)
                                            _tokenType = PdfTokenType.Number;
                                        else if (1 == _currentPosition - _startPosition && OBJECT_REFERENCE == _buffer[_startPosition])
                                            _tokenType = PdfTokenType.R;
                                        else if (NAME_START == _buffer[_startPosition])
                                            _tokenType = PdfTokenType.Name;
                                        else
                                            _tokenType = PdfTokenType.Token;
                                        finished = true;
                                        break;

                                    default:
                                        _currentPosition++;
                                        isNumber = isNumber & (current >= 48 && current <= 57);
                                        break;

                                }
                                break;

                            case LexerReadState.AfterAngleOpen:
                                // If we opened a '<' character, then if we find another one we will have found
                                // a dictionaryStart token. Otherwise, we are in the HexString state.
                                _currentPosition++;
                                if (ANGLE_OPEN == current)
                                {
                                    _tokenType = PdfTokenType.DictionaryStart;
                                    finished = true;
                                }
                                else
                                    readState = LexerReadState.HexString;

                                break;

                            case LexerReadState.AfterAngleClose:
                                // If we read a '>' character, then if we find another one we will have found
                                // a dictionaryEnd token. Otherwise, we are reading a normal token.
                                _currentPosition++;
                                if (ANGLE_CLOSE == current)
                                {
                                    _tokenType = PdfTokenType.DictionaryEnd;
                                    finished = true;
                                }
                                else
                                    readState = LexerReadState.Token;
                                break;

                            case LexerReadState.HexString:
                                // If we are inside a hex string, the string will end with the '>' token.
                                _currentPosition++;
                                if (ANGLE_CLOSE == current)
                                {
                                    _tokenType = PdfTokenType.HexString;
                                    finished = true;
                                }
                                break;

                            case LexerReadState.WhiteSpace:
                                // In the WhiteSpace state, if we find more whitespace we append it.
                                // If we find anything else we will have found the entire whitespace token.
                                switch (current)
                                {
                                    case WHITESPACE_ZERO:
                                    case WHITESPACE_TAB:
                                    case WHITESPACE_10:
                                    case WHITESPACE_12:
                                    case WHITESPACE_13:
                                    case WHITESPACE_SPACE:
                                        _currentPosition++;
                                        break;

                                    default:
                                        _tokenType = PdfTokenType.Whitespace;
                                        finished = true;
                                        break;
                                }
                                break;

                        } // switch readState
                    }
                    break;

                case PdfLexerMode.Binary:
                    while (false == finished)
                    {
                        // If we've fallen off the buffer we need to shuffle the current token to its beginning
                        // and fetch more data.
                        if (_currentPosition >= BUFFER_SIZE)
                        {
                            Array.Copy(_buffer, _startPosition, _buffer, 0, _currentPosition - _startPosition);
                            _currentPosition -= _startPosition;
                            _startPosition = 0;
                            int bytesRead = _stream.Read(_buffer, _currentPosition, BUFFER_SIZE - _currentPosition);
                            _endOfStream = _currentPosition + bytesRead;
                        }

                        if (_currentPosition >= _endOfStream) break;
                        current = _buffer[_currentPosition];

                        switch (readState)
                        {
                            case LexerReadState.Free:
                                // In the free state, we handle the following characters:
                                // whitespace - we enter the whitespace state
                                // any other - we enter the token state.
                                _currentPosition++;
                                switch (current)
                                {
                                    case WHITESPACE_ZERO:
                                    case WHITESPACE_TAB:
                                    case WHITESPACE_10:
                                    case WHITESPACE_12:
                                    case WHITESPACE_13:
                                    case WHITESPACE_SPACE:
                                        readState = LexerReadState.WhiteSpace;
                                        break;

                                    default:
                                        readState = LexerReadState.Token;
                                        break;
                                }
                                break;

                            case LexerReadState.WhiteSpace:
                                // In the whitespace state, we read whitespace until we find a token.
                                switch (current)
                                {
                                    case WHITESPACE_ZERO:
                                    case WHITESPACE_TAB:
                                    case WHITESPACE_10:
                                    case WHITESPACE_12:
                                    case WHITESPACE_13:
                                    case WHITESPACE_SPACE:
                                        _currentPosition++;
                                        break;

                                    default:
                                        // We found some non-whitespace. We preserve this character.
                                        finished = true;
                                        break;
                                }
                                break;

                            case LexerReadState.Token:
                                // In the token state, we read tokens until we find whitespace.
                                switch (current)
                                {
                                    case WHITESPACE_ZERO:
                                    case WHITESPACE_TAB:
                                    case WHITESPACE_10:
                                    case WHITESPACE_12:
                                    case WHITESPACE_13:
                                    case WHITESPACE_SPACE:
                                        // We found some whitespace. We preserve this character.
                                        finished = true;
                                        break;

                                    default:
                                        _currentPosition++;
                                        if (_currentPosition - _startPosition == BUFFER_SIZE) finished = true;
                                        break;
                                }
                                break;

                        }
                    }
                    break;

            } // switch mode 

            return true == finished;
        }
        #endregion



        // Public constructor
        // ==================
        #region Public constructor
        /// <summary>
        /// Constructs a new PdfLexer instance.
        /// </summary>
        /// <param name="stream">The stream to read from.</param>
        public PdfLexer(Stream stream)
        {
            if(null==stream) throw new ArgumentNullException("stream");

            _stream = stream;
            _buffer = new byte[BUFFER_SIZE];
            _startPosition = BUFFER_SIZE;
            _currentPosition = BUFFER_SIZE;
            _stateStack = new Stack<long>();
        }
        #endregion



        // internal types
        // ==============
        #region LexerReadState
        /// <summary>
        /// The LexerReadState enumeration lists the possible states that the lexer can be in while reading
        /// bytes off the stream.
        /// </summary>
        private enum LexerReadState
        {
            AfterAngleClose,
            AfterAngleOpen,
            Comment,
            Free,
            HexString,
            String,
            Token,
            WhiteSpace
        }
        #endregion

        #region PdfLexerStream
        /// <summary>
        /// The PdfLexerStream class represents a stream that reads from a PDF lexer's inner stream.
        /// The PfgLexerStream handles the safe opening and closing of a stream, so that the lexer can 
        /// continue to be used after the stream has been discarded.
        /// </summary>
        public class PdfLexerStream : Stream
        {
            // Private fields
            // ==============
            #region Private fields
            private int _bytesRead;
            private PdfLexer _lexer;
            private int _length;
            #endregion



            // Base class overrides
            // ====================
            #region Stream class overrides
            /// <summary>
            /// Indicates if the stream can read.
            /// </summary>
            public override bool CanRead
            {
                get { return true; }
            }

            /// <summary>
            /// Indicates if the stream can seek.
            /// </summary>
            public override bool CanSeek
            {
                get { return false; }
            }

            /// <summary>
            /// Indicates if the stream can write.
            /// </summary>
            public override bool CanWrite
            {
                get { return false; }
            }

            /// <summary>
            /// Closes the stream. This does not close the underlying stream, but resets the lexer buffer so
            /// that it can perform as normally.
            /// </summary>
            public override void Close()
            {
                // Close base implementation
                base.Close();

                // Reset the lexer buffer. This will force the next Read() operation on the lexer to refill
                // the buffer.
                _lexer._startPosition = BUFFER_SIZE;
                _lexer._currentPosition = BUFFER_SIZE;
            }

            /// <summary>
            /// Flushes the stream. This is not supported on a PdfLexer stream instance.
            /// </summary>
            public override void Flush()
            {
                throw new NotSupportedException();
            }

            /// <summary>
            /// Retrieves the length of the data, in bytes.
            /// </summary>
            public override long Length
            {
                get { return _length; }
            }

            /// <summary>
            /// Sets or retrieves the position of the stream. Setting the position is not supported
            /// on PdfLexer streams.
            /// </summary>
            public override long Position
            {
                get
                {
                    return _bytesRead;
                }
                set
                {
                    throw new NotSupportedException();
                }
            }

            /// <summary>
            /// Reads a number of bytes into the specified buffer. If the stream does not have all bytes
            /// available, it will read the maximum number of bytes it can read. If no data is available at all,
            /// this method returns 0 to indicate no further bytes can be read.
            /// </summary>
            /// <param name="buffer">The buffer to store data into</param>
            /// <param name="offset">The offset at which to store the data</param>
            /// <param name="count">The number of bytes to read.</param>
            /// <returns>The number of bytes that could be read.</returns>
            public override int Read(byte[] buffer, int offset, int count)
            {
                int bytesRead = 0;

                int bytesAvailable = _length - _bytesRead;
                if (bytesAvailable > 0)
                {
                    bytesRead = _lexer._stream.Read(buffer, offset, Math.Min(count, bytesAvailable));
                    _bytesRead += bytesRead;
                }

                return bytesRead;
            }

            /// <summary>
            /// Seeks the stream. Seeking is not supported on PdfLexer streams.
            /// </summary>
            /// <param name="offset">The offset to seek by</param>
            /// <param name="origin">The origin to start seeking from</param>
            /// <returns>The new position of the stream</returns>
            public override long Seek(long offset, SeekOrigin origin)
            {
                throw new NotSupportedException();
            }

            /// <summary>
            /// Updates the length of the stream. This is not supported on PdfLexer streams.
            /// </summary>
            /// <param name="value">The new length.</param>
            public override void SetLength(long value)
            {
                throw new NotSupportedException();
            }

            /// <summary>
            /// Writes bytes into the specified buffer. This is not supported on PdfLexer streams.
            /// </summary>
            /// <param name="buffer">The buffer to write into</param>
            /// <param name="offset">The offset in the buffer at which the copying starts</param>
            /// <param name="count">The number of bytes to copy</param>
            public override void Write(byte[] buffer, int offset, int count)
            {
                throw new NotSupportedException();
            }
            #endregion



            // public constructor
            // ==================
            #region Public constructor
            /// <summary>
            /// Constructs a new PdfLexerStream instance.
            /// </summary>
            /// <param name="lexer">The lexer to manage</param>
            /// <param name="length">The maximum number of bytes we can read</param>
            public PdfLexerStream(PdfLexer lexer, int length)
            {
                _lexer = lexer;
                _length = length;

                // Point the stream at the current location.
                lexer._stream.Seek(-(lexer._endOfStream-lexer._currentPosition), SeekOrigin.Current);
            }
            #endregion

        } // PdfLexerStream class

        #endregion

    } // PdfLexer class
}
